use std::cmp::Ordering;
use std::ops::Add;

/// A node in the binary tree.
#[derive(Debug)]
struct Node<T: Ord> {
    value: T,
    left: Subtree<T>,
    right: Subtree<T>,
}

/// A possibly-empty subtree.
#[derive(Debug)]
struct Subtree<T: Ord>(Option<Box<Node<T>>>);

/// A container storing a set of values, using a binary tree.
///
/// If the same value is added multiple times, it is only stored once.
#[derive(Debug)]
pub struct BinaryTree<T: Ord> {
    root: Subtree<T>,
}

impl<T: Ord> Subtree<T> {
    pub fn insert(&mut self, value: T) {
        match &mut self.0 {
            None => {
                let r = Node { value, left: Subtree(None), right: Subtree(None) };
                self.0 = Some(Box::new(r))
            }
            Some(ref mut node) => {
                match value.cmp(&node.value) {
                    Ordering::Less => node.left.insert(value),
                    Ordering::Greater => node.right.insert(value),
                    _ => {}
                }
            }
        }
    }
}

impl<T: Ord> BinaryTree<T> {
    pub fn new() -> BinaryTree<T> {
        BinaryTree { root: Subtree(None) }
    }

    pub fn insert(&mut self, value: T) {
        self.root.insert(value);
    }

    pub fn len(&self) -> u32 {
        let mut counter = 0;
        count(&self.root.0, &mut counter);
        counter
    }

    pub fn has(&self, val: &T) -> bool {
        find(&self.root.0, val)
    }
}

pub fn count<T: Ord>(val: &Option<Box<Node<T>>>, counter: &mut u32) {
    match val {
        Some(node) => {
            *counter += 1;
            count(&node.left.0, counter);
            count(&node.right.0, counter);
        }
        None => {}
    }
}

pub fn find<T: Ord>(val: &Option<Box<Node<T>>>, dest: &T) -> bool {
    match val {
        Some(node) => {
            match node.value.cmp(&dest) {
                Ordering::Less => find(&node.right.0, dest),
                Ordering::Equal => true,
                Ordering::Greater => find(&node.left.0, dest)
            }
        }
        None => false
    }
}

// Implement `new`, `insert`, `len`, and `has`.

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn len() {
        let mut tree = BinaryTree::new();
        assert_eq!(tree.len(), 0);
        tree.insert(2);
        assert_eq!(tree.len(), 1);
        tree.insert(1);
        assert_eq!(tree.len(), 2);
        tree.insert(2); // not a unique item
        assert_eq!(tree.len(), 2);
    }

    #[test]
    fn has() {
        let mut tree = BinaryTree::new();
        fn check_has(tree: &BinaryTree<i32>, exp: &[bool]) {
            let got: Vec<bool> =
                (0..exp.len()).map(|i| tree.has(&(i as i32))).collect();
            assert_eq!(&got, exp);
        }

        check_has(&tree, &[false, false, false, false, false]);
        tree.insert(0);
        check_has(&tree, &[true, false, false, false, false]);
        tree.insert(4);
        check_has(&tree, &[true, false, false, false, true]);
        tree.insert(4);
        check_has(&tree, &[true, false, false, false, true]);
        tree.insert(3);
        check_has(&tree, &[true, false, false, true, true]);
    }

    #[test]
    fn unbalanced() {
        let mut tree = BinaryTree::new();
        for i in 0..100 {
            tree.insert(i);
        }
        assert_eq!(tree.len(), 100);
        assert!(tree.has(&50));
    }
}